package model;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JOptionPane;

import persistence.KnowBase;
import controller.IaMove;

/**
 * Jogador de IA com experiência.
 *
 * Esse jogador é semelhante ao EasyIA, exceto pelo fato de que ele tem uma base
 * de conhecimento própria, que ele vai construindo à medida que vai jogando.
 * Esse jogador é uma tentativa de mostrar de forma simples os conceitos de
 * Aprendizado de Máquina. O jogador vai anotando em sua base de conhecimento
 * quais jogadas foram bem sucedidas e quais não foram. Depois de muitas
 * partidas, espera-se que ele consiga jogar razoavelmente bem.
 */
public class HardIA extends Player {
	
	/**
	 * A base de conhecimento desse jogador.
	 */
	KnowBase knowledge = new KnowBase();

	/**
	 * Um gerador de números aleatório para sortear a jogada.
	 */
	private Random rand = new Random();

	/**
	 * Lista das jogadas executadas.
	 */
	private ArrayList<String> choosedStates = new ArrayList<String>();
	
	/**
	 * Pontuação de cada jogada executada.
	 */
	private ArrayList<Integer> choosedScores = new ArrayList<Integer>();

	/**
	 * Constrói um HardIA, que já estará pronto para jogar.
	 */
	public HardIA(PieceType piece) {
		super(piece);
		setReady(true);
	}

	@Override
	public void play(Board board) {
		// Vetor para guardar os movimentos possíveis.
		ArrayList<Point> moves = new ArrayList<Point>();
		
		/* Verifica para cada coluna se é possível jogar nela. */
		for (int j = 0; j < board.getPieces()[0].length; j++) {
			if ( ! board.getPieces()[0][j].equals(PieceType.NONE)) continue;
			for (int i = board.getPieces().length - 1; i >= 0; i--) {
				if (board.getPieces()[i][j].equals(PieceType.NONE)) {
					moves.add(new Point(i, j));
					break;
				}
			}
		}
		
		/* Garante que encontrou pelo menos uma jogada possível. */
		if (moves.size() == 0) return;
		
		/* Tenta encontrar uma jogadas que vai fazer ganhar imediatamente. */
		for (Point p : moves) {
			board.setPiece(p, PieceType.PLAYER2);
			if (Rules.gameOver(board)) {
				new IaMove(p);
				return;
			}
			board.setPiece(p, PieceType.NONE);
		}
		
		/* Tenta encontrar uma jogada que vai impedir o outro de ganhar. */
		for (Point p : moves) {
			board.setPiece(p, PieceType.PLAYER1);
			if (Rules.gameOver(board)) {
				board.setPiece(p, PieceType.PLAYER2);
				new IaMove(p);
				return;
			}
			board.setPiece(p, PieceType.NONE);
		}

		/*
		 * Agora vem o aprendizado de máquina. Para cada uma das jogadas
		 * possíveis, o computador consulta sua base de conhecimento e verifica
		 * qual a pontuação da jogada. As jogadas que tem pontuação maior tem
		 * mais chance de serem escolhidas. Toda jogada feita fica armazenada
		 * até o fim da partida. Se o computador ganhar a partida, quer dizer
		 * que as jogadas escolhidas por ele foram bem sucedidas, então ele
		 * aumenta a pontuação de cada uma na sua base de conhecimento.
		 */
		ArrayList<Integer> scores = new ArrayList<Integer>();
		int total = 0;
		for (Point p : moves) {
			board.setPiece(p, PieceType.PLAYER2);
			int s = knowledge.getScore(board);
			total += s;
			scores.add(s);
			board.setPiece(p, PieceType.NONE);
		}
		int random = rand.nextInt(total);
		int i = 0;
		int soma = scores.get(i);
		while (soma < random) {
			i++;
			soma += scores.get(i);
		}
		board.setPiece(moves.get(i), PieceType.PLAYER2);
		choosedStates.add(board.toString());
		choosedScores.add(scores.get(i));
		new IaMove(moves.get(i));
	}

	@Override
	public void youWin() {
		for (int i = 0; i < choosedStates.size(); i++) {
			knowledge.saveScore(choosedStates.get(i), choosedScores.get(i) + 1);
		}
		JOptionPane.showMessageDialog(null, "Você perdeu.", "Game Over", JOptionPane.WARNING_MESSAGE);
	}

}
